﻿#region Libraries
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Perspective.Wpf3D.Shapes;
using Microsoft.Win32;
using System.Diagnostics;
using System.Threading;
using System.Windows.Threading;
using System.IO;
using System.Windows.Interop;
using System.ComponentModel;
#endregion

namespace LabyWPF3D
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow : Window
	{
		#region Properties
		/// <summary>
		/// Labyrinth instance
		/// </summary>
		private Labyrinth labyrinth;
		/// <summary>
		/// Coordinates
		/// </summary>
		private int[,] coord;
		/// <summary>
		/// Correct path
		/// </summary>
		private int[,] solutionPath;
		/// <summary>
		/// Is the console panel visible
		/// </summary>
		public bool isConsoleVisible = true;
		/// <summary>
		/// Is the cammera control panel visible
		/// </summary>
		private bool isCameraControlVisible = true;
		/// <summary>
		/// Previous mouse position
		/// </summary>
		private Point prevPosition;
		/// <summary>
		/// Is labyrinth solved
		/// </summary>
		private bool isSolved = false;
		/// <summary>
		/// Background worker
		/// </summary>
		private BackgroundWorker worker;
		/// <summary>
		/// Material for road:
		/// </summary>
		private DiffuseMaterial roadMaterial;
		/// <summary>
		/// Finish line material
		/// </summary>
		private DiffuseMaterial finishMaterial;
		/// <summary>
		/// Labyrinth wall material
		/// </summary>
		private DiffuseMaterial wallMaterial;
		/// <summary>
		/// Moving figure material
		/// </summary>
		private DiffuseMaterial ballMaterial;
		/// <summary>
		/// Correct path material
		/// </summary>
		private DiffuseMaterial correctPathMaterial;
		/// <summary>
		/// Moving ball
		/// </summary>
		private Football3D ball;
		/// <summary>
		/// Ball position
		/// </summary>
		private TranslateTransform3D ballPosition;
		/// <summary>
		/// Ball rotation angle
		/// </summary>
		private AxisAngleRotation3D ballRotation;
		/// <summary>
		/// Rotation tranformation
		/// </summary>
		private RotateTransform3D rotateTransform;
		/// <summary>
		/// Rotation animation
		/// </summary>
		private Rotation3DAnimation rotationAnimation;
		/// <summary>
		/// Is algorithm currently running
		/// </summary>
		private bool isRunning = false;
		/// <summary>
		/// Loaded file name
		/// </summary>
		private string loadedFile;

		#endregion

		#region Constructor and other initiation methods
		/// <summary>
		/// Class constructor
		/// </summary>
		public MainWindow()
		{
			InitializeComponent();
			InitMaterials();
			rotationAnimation = new Rotation3DAnimation();
			Log("Program started...");
			labyrinth = new Labyrinth();
			coord = new int[Labyrinth.MAX_ROWS, Labyrinth.MAX_COLS];
			Log("Components successfully initialized...");
			InitWorker();
		}

		/// <summary>
		/// Iniates background worker
		/// </summary>
		public void InitWorker() {
			worker = new BackgroundWorker();
			worker.RunWorkerCompleted += delegate(object sendera, RunWorkerCompletedEventArgs ea)
			{
				worker_RunWorkerCompleted(sendera, ea, labyrinth);
			};
			worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
			worker.DoWork += new DoWorkEventHandler(labyrinth.Solve);
			worker.WorkerReportsProgress = true;
			worker.WorkerSupportsCancellation = true;
		}

		/// <summary>
		/// Initiates materials/textures
		/// </summary>
		public void InitMaterials()
		{
			// Road material:
			roadMaterial = new DiffuseMaterial();
			ImageBrush brush = new ImageBrush();
			brush.ImageSource = new BitmapImage(new Uri("pack://application:,,,/LabyWPF3D;component/Images/road.jpg"));
			roadMaterial.Brush = brush;
			
			// Figure material:
			ballMaterial = new DiffuseMaterial();
			brush = new ImageBrush();
			brush.ImageSource = new BitmapImage(new Uri("pack://application:,,,/LabyWPF3D;component/Images/ball.jpg"));
			ballMaterial.Brush = brush;

			// Finish material:
			finishMaterial = new DiffuseMaterial();
			brush = new ImageBrush();
			brush.ImageSource = new BitmapImage(new Uri("pack://application:,,,/LabyWPF3D;component/Images/finish.jpg"));
			finishMaterial.Brush = brush;

			// Wall material:
			wallMaterial = new DiffuseMaterial();
			brush = new ImageBrush();
			brush.ImageSource = new BitmapImage(new Uri("pack://application:,,,/LabyWPF3D;component/Images/wall.jpg"));
			wallMaterial.Brush = brush;

			// Correct path material:
			correctPathMaterial = new DiffuseMaterial();
			SolidColorBrush colorBrush = new SolidColorBrush(Color.FromRgb(205, 38, 38));
			colorBrush.Opacity = 0.3;
			correctPathMaterial.Brush = colorBrush;
		}
		#endregion

		#region Drawing methods
		/// <summary>
		/// Draws the labyrinth walls
		/// </summary>
		public void DrawLabyrinth()
		{
			// Coordinates:
			int x = 0;
			int y = 0;
			int z = 0;

			// Loop through rows:
			for (int i = 0; i < labyrinth.GetRows(); i++)
			{
				// Loop through columns:
				for (int j = 0; j < labyrinth.GetCols(); j++)
				{
					// Set the coordinates:
					x = j;
					y = 0;
					z = i;
					// We found a wall of the labyrinth:
					if (labyrinth.GetCell(i, j) == Labyrinth.WALL)
					{
						DrawWall(x, y, z);
					}
					else
					{
						// We found beginning:
						if (labyrinth.GetCell(i, j) == Labyrinth.START)
						{
							// Draw the ball here:
							DrawBall(x, y, z);
						}
						else
						{
							// We found finish:
							if (labyrinth.GetCell(i, j) == Labyrinth.FINISH)
							{
								DrawFinish(x, y, z);
							}
							else
							{
								// There's road:
								if (labyrinth.GetCell(i, j) == Labyrinth.PATH)
								{
									DrawRoad(x, y, z);
								}
							}
						}
					}
				}
			}
		}

		/// <summary>
		/// Draws the ball
		/// </summary>
		/// <param name="x">X coordinate</param>
		/// <param name="y">Y coordinate</param>
		/// <param name="z">Z coordinate</param>
		protected void DrawBall(int x, int y, int z)
		{
			// Create the ball:
			ball = new Football3D();
			//ball.Material = ballMaterial;
			//ball.ParallelCount = 3;
			// Set position and size of the ball:
			Transform3DGroup transGroup = new Transform3DGroup();
			transGroup.Children.Add(new ScaleTransform3D(new Vector3D(0.5, 0.5, 0.5)));
			ballPosition = new TranslateTransform3D(new Vector3D(x + 0.5, y + 0.5, z + 0.5));
			ballRotation = new AxisAngleRotation3D();
			rotateTransform = new RotateTransform3D();
			rotateTransform.Rotation = ballRotation;
			transGroup.Children.Add(ballPosition);
			transGroup.Children.Add(rotateTransform);
			// Insert the ball:
			ball.Transform = transGroup;
			Figure.Children.Add(ball);
			DrawRoad(x, y, z);
		}

		/// <summary>
		/// Draws part of road
		/// </summary>
		/// <param name="x">X coordinate</param>
		/// <param name="y">Y coordinate</param>
		/// <param name="z">Z coordinate</param>
		protected void DrawRoad(int x, int y, int z)
		{
			Box3D box = new Box3D();
			box.Material = roadMaterial;
			Transform3DGroup transGroup = new Transform3DGroup();
			transGroup.Children.Add(new ScaleTransform3D(new Vector3D(1, 0, 1)));
			transGroup.Children.Add(new TranslateTransform3D(new Vector3D(x, y, z)));
			box.Transform = transGroup;
			Path.Children.Add(box);
		}

		/// <summary>
		/// Draws wall
		/// </summary>
		/// <param name="x">X coordinate</param>
		/// <param name="y">Y coordinate</param>
		/// <param name="z">Z coordinate</param>
		protected void DrawWall(int x, int y, int z)
		{
			Box3D box = new Box3D();
			box.Material = wallMaterial;
			Transform3DGroup transGroup = new Transform3DGroup();
			transGroup.Children.Add(new ScaleTransform3D(new Vector3D(1, 1, 1)));
			transGroup.Children.Add(new TranslateTransform3D(new Vector3D(x, y, z)));
			box.Transform = transGroup;
			Walls.Children.Add(box);
		}
		/// <summary>
		/// Draws finish
		/// </summary>
		/// <param name="x">X coordinate</param>
		/// <param name="y">Y coordinate</param>
		/// <param name="z">Z coordinate</param>
		protected void DrawFinish(int x, int y, int z)
		{
			Box3D box = new Box3D();
			box.Material = finishMaterial;
			Transform3DGroup transGroup = new Transform3DGroup();
			transGroup.Children.Add(new ScaleTransform3D(new Vector3D(1, 0, 1)));
			transGroup.Children.Add(new TranslateTransform3D(new Vector3D(x, y, z)));
			box.Transform = transGroup;
			Path.Children.Add(box);
		}

		/// <summary>
		/// Adds correct path layer to path
		/// </summary>
		public void DrawCorrectPath()
		{
			// Coordinates:
			int x = 0;
			int y = 0;
			int z = 0;

			// Loop through rows:
			for (int i = 0; i < labyrinth.GetRows(); i++)
			{
				// Loop through columns:
				for (int j = 0; j < labyrinth.GetCols(); j++)
				{
					// Set the coordinates:
					x = j;
					y = 0;
					z = i;
					// Draw floor:
					if ((solutionPath[i, j] & Labyrinth.POSSIBLE) != 0)
					{
                        System.Console.Write(solutionPath[i, j]+" ");
						Box3D box = new Box3D();
						box.Material = correctPathMaterial;
						Transform3DGroup transGroup = new Transform3DGroup();
						transGroup.Children.Add(new ScaleTransform3D(new Vector3D(1, 0, 1)));
						transGroup.Children.Add(new TranslateTransform3D(new Vector3D(x, y + 0.2, z)));
						box.Transform = transGroup;
						CorrectPath.Children.Add(box);
					}
				}
                System.Console.WriteLine();
			}
		}
		#endregion

		#region Animations
		/// <summary>
		/// Moves 3D ball
		/// </summary>
		/// <param name="data">Information about the move</param>
		private void MoveBall(notification data)
		{
			DoubleAnimation animation = new DoubleAnimation();
			animation.FillBehavior = FillBehavior.HoldEnd;

			Vector3D axisVector = new Vector3D();

			// Animate to the specified direction:
			switch (data.currentPosition.front)
			{
				case Compass.East:
					animation.From = (double)(ballPosition.OffsetX);
					animation.To = data.currentPosition.col + 0.5;
					animation.By = 0.1;
					ballPosition.OffsetX = data.currentPosition.col + 0.5;
					//ball.BeginAnimation(TranslateTransform3D.OffsetXProperty, animation);
					axisVector = new Vector3D(1, 0, 0);
					break;
				case Compass.West:
					animation.From = (double)(ballPosition.OffsetX);
                    animation.To = data.currentPosition.col + 0.5;
					animation.By = 0.1;
					ballPosition.OffsetX = data.currentPosition.col + 0.5;
					//ball.BeginAnimation(TranslateTransform3D.OffsetXProperty, animation);
					axisVector = new Vector3D(-1,0,0);
					break;
				case Compass.South:
					animation.From = (double)(ballPosition.OffsetZ);
                    animation.To = data.currentPosition.row + 0.5;
					animation.By = 0.1;
					ballPosition.OffsetZ = data.currentPosition.row + 0.5;
					//ball.BeginAnimation(TranslateTransform3D.OffsetYProperty, animation);
					axisVector = new Vector3D(0, 0, 1);
					break;
				case Compass.North:
					animation.From = (double)(ballPosition.OffsetZ);
                    animation.To = data.currentPosition.row + 0.5;
					animation.By = 0.1;
					ballPosition.OffsetZ = data.currentPosition.row + 0.5;
					//ball.BeginAnimation(TranslateTransform3D.OffsetYProperty, animation);
					axisVector = new Vector3D(0, 0, -1);
					break;
			}
			// Rotate:
			rotateTransform.CenterX = data.currentPosition.col + 0.5;
			rotateTransform.CenterY = 0.5;
			rotateTransform.CenterZ = data.currentPosition.row + 0.5;
			ballRotation = new AxisAngleRotation3D(axisVector, ballRotation.Angle - 180);

			/*AxisAngleRotation3D asd = new AxisAngleRotation3D(new Vector3D(0,-1,0), 1);
			//ballRotation.Axis = asd.Axis;*/
            rotationAnimation.To = ballRotation;
			rotationAnimation.SpeedRatio = 2;
			rotationAnimation.FillBehavior = FillBehavior.HoldEnd;
		//	rotationAnimation.RepeatBehavior = RepeatBehavior.Forever;
			//rotateTransform.BeginAnimation(RotateTransform3D.RotationProperty, null);
			rotateTransform.BeginAnimation(RotateTransform3D.RotationProperty, rotationAnimation);
			//DoubleAnimation rotationAnimation = new DoubleAnimation(ballRotation, new Duration(TimeSpan.FromSeconds(1.2)));
			//ballRotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, rotationAnimation);
		}

		/// <summary>
		/// Hides/shows console window
		/// </summary>
		public void ToogleConsole()
		{
			DoubleAnimation animation = new DoubleAnimation();
			animation.Duration = new Duration(TimeSpan.FromSeconds(1.0));
			animation.FillBehavior = FillBehavior.HoldEnd;
			animation.From = ConsoleTab.Height;
			if (isConsoleVisible)
			{
				animation.To = 25;
			}
			else
			{
				animation.To = 200;
			}
			ConsoleTab.BeginAnimation(TabControl.HeightProperty, animation);
			ConsoleMenuItem.IsChecked = isConsoleVisible =  !isConsoleVisible;
		}

		/// <summary>
		/// Hides/shows camera control window
		/// </summary>
		public void ToogleCameraControl()
		{
			DoubleAnimation animation = new DoubleAnimation();
			animation.Duration = new Duration(TimeSpan.FromSeconds(1.0));
			animation.FillBehavior = FillBehavior.HoldEnd;
			animation.From = CameraTab.Width;
			if (isCameraControlVisible)
			{
				animation.To = 30;
			}
			else
			{
				animation.To = 160;
			}
			CameraTab.BeginAnimation(TabControl.WidthProperty, animation);
			CameraMenuItem.IsChecked = isCameraControlVisible = !isCameraControlVisible;
		}
		#endregion

		#region Event Handlers
		/// <summary>
		/// Toogle console click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void ConsoleMenuItem_Click(object sender, RoutedEventArgs e)
		{
			ToogleConsole();
		}

		/// <summary>
		/// Toogle camera click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void CameraMenuItem_Click(object sender, RoutedEventArgs e)
		{
			ToogleCameraControl();
		}

		/// <summary>
		/// Toogle 3D axis click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void AxisMenuItem_Click(object sender, RoutedEventArgs e)
		{
			if (Axis.Visibility == Visibility.Hidden)
			{
				Axis.Visibility = Visibility.Visible;
				AxisMenuItem.IsChecked = true;
			}
			else
			{
				Axis.Visibility = Visibility.Hidden;
				AxisMenuItem.IsChecked = false;
			}
		}
		/// <summary>
		/// Console header click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void TabItem_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			ToogleConsole();
		}

		/// <summary>
		/// Viewport mouse wheel
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void Viewport3D_MouseWheel(object sender, MouseWheelEventArgs e)
		{
			// Zooom in:
			if (e.Delta > 0)
			{
				sliderZoom.Value++;
			}
			// Zoom out:
			else
			{
				sliderZoom.Value--;
			}
		}

		/// <summary>
		/// Camera control header click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void TabItem_MouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
		{
			ToogleCameraControl();
		}

		/// <summary>
		/// Exit menu item click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void MenuItem_Click(object sender, RoutedEventArgs e)
		{
			Close();
		}

		/// <summary>
		/// Mouse drag
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void Canvas_MouseMove(object sender, MouseEventArgs e)
		{
			// Is the left mouse button clicked:
			if (e.LeftButton == MouseButtonState.Pressed)
			{
				// Move by X:
				if (e.GetPosition(null).X > prevPosition.X)
				{
					sliderX1.Value -= 0.05;
				}
				else
				{
					sliderX1.Value += 0.05;
				}
				// Move by Y:
				if (e.GetPosition(null).Y > prevPosition.Y)
				{
					sliderY1.Value += 0.05;
				}
				else
				{
					sliderY1.Value -= 0.05;
				}
				prevPosition = e.GetPosition(null);
			}
		}

		/// <summary>
		/// Left mouse button press
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			// Change mouse cursor:
			Canvas.Cursor = Cursors.Hand;
		}

		/// <summary>
		/// Left mouse button release
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			// Restore default mouse button:
			Canvas.Cursor = Cursors.Arrow;
		}

		/// <summary>
		/// Open labyrinth menu item click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void OpenLabMenuItem_Click(object sender, RoutedEventArgs e)
		{
			// Show file open dialog:
			OpenFileDialog dialog = new OpenFileDialog();
			dialog.Title = "Select labyrinth file to open";
			dialog.InitialDirectory = System.IO.Directory.GetCurrentDirectory() + "\\Data";
			dialog.Filter = "Labyrinth files (*.lab) | *.lab";
			dialog.ValidateNames = true;
			// If valid file was selected:
			if (dialog.ShowDialog() == true)
			{
				// Reset everything:
				Reset();
				loadedFile = dialog.FileName;
				labyrinth.useSolution = false;
				labyrinth.Clear();
				// Init labyrinth:
				labyrinth.ReadFromFile(dialog.FileName);
				Log("Labyrinth (" + labyrinth.GetRows() + "x" + labyrinth.GetCols() + ") successfully loaded from file '" + System.IO.Path.GetFileName(dialog.FileName) + "'...");
				// Draw labyrinth:
				Stopwatch timer = new Stopwatch();
				timer.Start();
				DrawLabyrinth();
				timer.Stop();
				Log("Labyrinth successfully drawed. It took " + timer.Elapsed.Seconds + " seconds and " + timer.Elapsed.Milliseconds + " miliseconds to complete...");
				// Enable extra menu items:
				OpenSolutionMenuItem.IsEnabled = ExecMenuItem.IsEnabled = ReloadMenuItem.IsEnabled = true;
				SaveSolutionMenuItem.IsEnabled = SolutionMenuItem.IsEnabled = CorrectPathMenuItem.IsEnabled = false;
			}
			else
			{
				Log("Labyrinth not loaded...");
			}
		}

		/// <summary>
		/// Open solution menu item click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void OpenSolutionMenuItem_Click(object sender, RoutedEventArgs e)
		{
			// Show file open dialog:
			OpenFileDialog dialog = new OpenFileDialog();
			dialog.Title = "Select solution file to open";
			dialog.InitialDirectory = System.IO.Directory.GetCurrentDirectory() + "\\Data";
			dialog.Filter = "Solution files (*.sol) | *.sol";
			dialog.ValidateNames = true;
			// If valid file was selected:
			if (dialog.ShowDialog() == true)
			{
				// Load solution:
				labyrinth.ReadSolutionFromFile(dialog.FileName);
				solutionPath = labyrinth.GetSolution();
				Log("Labyrinth solution loaded from file '" + System.IO.Path.GetFileName(dialog.FileName) + "'...");
				// Enable solution using:
				isSolved = labyrinth.useSolution = true;
				SolutionMenuItem.IsEnabled = SolutionMenuItem.IsChecked = CorrectPathMenuItem.IsEnabled = true;
				CorrectPath.Children.Clear();
				DrawCorrectPath();
				Log("Loaded solution will be used...");
			}
			else
			{
				Log("Labyrinth solution not loaded...");
			}
		}

		/// <summary>
		/// Save solution menu click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void SaveSolutionMenuItem_Click(object sender, RoutedEventArgs e)
		{
			// Show file save dialog:
			SaveFileDialog dialog = new SaveFileDialog();
			dialog.Title = "Select solution file to save to";
			dialog.InitialDirectory = System.IO.Directory.GetCurrentDirectory() + "\\Data";
			dialog.Filter = "Solution files (*.sol) | *.sol";
			dialog.OverwritePrompt = true;
			// If file selected:
			if (dialog.ShowDialog() == true)
			{
				// Save to file:
				labyrinth.SaveSolutionToFile(dialog.FileName);
				Log("Solution successfully saved to file '" + System.IO.Path.GetFileName(dialog.FileName) + "'...");
			}
		}

		/// <summary>
		/// Execution menu item click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void ExecMenuItem_Click(object sender, RoutedEventArgs e)
		{
			// Solve the algorythm:
			Log("Begin solving the algorythm...");
			isRunning = true;
			worker.RunWorkerAsync(labyrinth);
			SaveSolutionMenuItem.IsEnabled = isSolved;
			StopMenuItem.IsEnabled = true;
		}

		/// <summary>
		/// Stop execution menu click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void StopMenuItem_Click(object sender, RoutedEventArgs e)
		{
			worker.CancelAsync();
			StopMenuItem.IsEnabled = false;
		}

		/// <summary>
		/// About menu click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void MenuItem_Click_1(object sender, RoutedEventArgs e)
		{
			// Show about window as modal dialog:
			AboutWindow about = new AboutWindow();
			WindowInteropHelper helper = new WindowInteropHelper(about);
			helper.Owner = new WindowInteropHelper(Application.Current.MainWindow).Handle;
			about.ShowDialog();
		}
		
		/// <summary>
		/// Tryin to close the window
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void window_Closing(object sender, CancelEventArgs e)
		{
			if (isRunning) {
				e.Cancel = (MessageBox.Show("Labyrinth is still running. Are you sure you want to exit?", "Exit confirmation", MessageBoxButton.YesNo) == MessageBoxResult.No);
			}
		}

		/// <summary>
		/// Reload menu click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void ReloadMenuItem_Click(object sender, RoutedEventArgs e)
		{
			// Reset everything:
			Reset(true);
			labyrinth.useSolution = false;
			labyrinth.Clear();
			// Init labyrinth:
			labyrinth.ReadFromFile(loadedFile);
			// Draw labyrinth:
			DrawLabyrinth();
			Log("Labyrinth (" + labyrinth.GetRows() + "x" + labyrinth.GetCols() + ") successfully reloaded...");
			// Enable extra menu items:
			OpenSolutionMenuItem.IsEnabled = ExecMenuItem.IsEnabled = true;
			labyrinth.useSolution = SolutionMenuItem.IsChecked;
		}

		/// <summary>
		/// Toogle correct path click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void CorrectPathMenuItem_Click(object sender, RoutedEventArgs e)
		{
			if (!CorrectPathMenuItem.IsChecked)
			{
				CorrectPath.Transform = new ScaleTransform3D(new Vector3D(0, 0, 0));
			}
			else
			{
				CorrectPath.Transform = new ScaleTransform3D(new Vector3D(1, 1, 1));
			}
		}

		/// <summary>
		/// Use previous solution click
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Event</param>
		private void SolutionMenuItem_Click(object sender, RoutedEventArgs e)
		{
			labyrinth.useSolution = SolutionMenuItem.IsChecked;
			if (!SolutionMenuItem.IsChecked)
			{
				Log("Solution will not be used...");
			}
			else
			{
				Log("Current solution will be used...");
			}
		}

		/// <summary>
		/// Worker progress update
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Progress event</param>
		private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			notification report = (notification)e.UserState;
			// Add log entry:
			switch (report.type) {
				case Notification.BeforeMove:
					Log("Object current coordinates are " + report.currentPosition.row + "x" + report.currentPosition.col + "...");
					Log("Object Trying to move to " + report.direction + "...");
					break;
				case Notification.AfterSuccessfullMove:
					Log("Object have moved to " + report.direction + "...");
					Log("Object new coordinates are " + report.currentPosition.row + "x" + report.currentPosition.col + "...");
					Log("Object front is on " + report.currentPosition.front + " now...");
					// Move ball:
					MoveBall(report);
					break;
				case Notification.AfterFailedMove:
					Log("Object can't move to " + report.direction);
					Log("Object coordinates remain as " + report.currentPosition.row + "x" + report.currentPosition.col + "...");
					Log("Object front is on " + report.currentPosition.front + " now...");
					break;
			}
		}

		/// <summary>
		/// Worker job completed
		/// </summary>
		/// <param name="sender">Object</param>
		/// <param name="e">Progress event</param>
		private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e, Labyrinth state)
		{
			// If canceled:
			if (e.Cancelled)
			{
				Log("Solution not found since solving was canceled");
			}
			else
			{
				TimeSpan time = (TimeSpan)state.timer.Elapsed;
				Log("Solution founded at " + state.GetCurrentPosition().row + "x" + state.GetCurrentPosition().col + ". It took " + time.Seconds + " seconds and " + time.Milliseconds + " miliseconds to complete...");
				// Draw correct path if it isn't already draw:
				solutionPath = labyrinth.GetGenetatedSolution();
				if (!CorrectPathMenuItem.IsEnabled)
					DrawCorrectPath();
				// Enable use save solution:
				StopMenuItem.IsEnabled = ExecMenuItem.IsEnabled = false;
				SaveSolutionMenuItem.IsEnabled = SolutionMenuItem.IsEnabled = CorrectPathMenuItem.IsEnabled = true;
			}
			isRunning = false;
		}

        /// <summary>
        /// Latency Edit Click
        /// </summary>
        /// <param name="sender">Object</param>
        /// <param name="e">Event</param>
        private void EditLatencyMenuItem_Click(object sender, RoutedEventArgs e)
        {
            // Create new latency window
            LatencyWindow latencyWindow = new LatencyWindow(labyrinth);
            WindowInteropHelper helper = new WindowInteropHelper(latencyWindow);
            helper.Owner = new WindowInteropHelper(Application.Current.MainWindow).Handle;
            latencyWindow.ShowDialog();
        }

		#endregion

		#region Various Methods
		/// <summary>
		/// Adds entry to the log console
		/// </summary>
		/// <param name="text">Log text</param>
		public void Log(String text)
		{
			Cons.Text += DateTime.Now.ToString("hh:mm:ss") + ": " + text + "\n";
			consoleScrollBar.ScrollToBottom();
		}

		/// <summary>
		/// Resets labyrinth data
		/// </summary>
		/// <param name="bool">Reset path</param>
		public void Reset(bool resetPath = false)
		{
			if (!resetPath) {
				CorrectPath.Children.Clear();
			}
			Walls.Children.Clear();
			Path.Children.Clear();
			Figure.Children.Clear();
		}
		#endregion

        
	}
}